2 * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "unittest_common.h"
18 #include "mDNSMacOSX.h"
19 #import <XCTest/XCTest.h>
21 // This query request message was generated from the following command: "dns-sd -lo -timeout -Q cardinal2.apple.com. A"
22 char query_req_msgbuf[33]= {
23 0x00, 0x01, 0x90, 0x00,
24 // DNSServiceFlags.L = (kDNSServiceFlagsReturnIntermediates |kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout)
25 0xff, 0xff, 0xff, 0xff,
26 // interfaceIndex = mDNSInterface_LocalOnly
27 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c,
28 0x32, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x00, 0x00, 0x01, 0x00,
32 mDNSlocal mStatus InitEtcHostsRecords(void)
34 mDNS *m = &mDNSStorage;
35 struct sockaddr_storage hostaddr;
38 mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
40 memset(&hostaddr, 0, sizeof(hostaddr));
41 get_ip("127.0.0.1", &hostaddr);
44 MakeDomainNameFromDNSNameString(&domain, "localhost");
46 mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
48 memset(&hostaddr, 0, sizeof(hostaddr));
49 get_ip("0000:0000:0000:0000:0000:0000:0000:0001", &hostaddr);
51 MakeDomainNameFromDNSNameString(&domain, "localhost");
53 mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
55 memset(&hostaddr, 0, sizeof(hostaddr));
56 get_ip("255.255.255.255", &hostaddr);
58 MakeDomainNameFromDNSNameString(&domain, "broadcasthost");
60 mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
62 memset(&hostaddr, 0, sizeof(hostaddr));
63 get_ip("17.226.40.200", &hostaddr);
65 MakeDomainNameFromDNSNameString(&domain, "cardinal2.apple.com");
67 mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
68 UpdateEtcHosts_ut(&newhosts);
70 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
73 return mStatus_NoError;
76 @interface LocalOnlyATimeoutTest : XCTestCase
78 request_state* client_request_message;
79 UDPSocket* local_socket;
80 char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
84 @implementation LocalOnlyATimeoutTest
86 // The InitUnitTest() initializes a minimal mDNSResponder environment as
87 // well as allocates memory for a local_socket and client request.
88 // It also sets the domainname_cstr specified in the client's query request.
89 // Note: This unit test does not send packets on the wire and it does not open sockets.
93 mStatus result = init_mdns_storage();
94 XCTAssertEqual(result, mStatus_NoError);
96 // Allocate a client request
97 local_socket = (UDPSocket *)calloc(1, sizeof(*local_socket));
99 // Allocate memory for a request that is used to make client requests.
100 client_request_message = calloc(1, sizeof(request_state));
102 // Init domainname that is used by unit tests
103 strlcpy(domainname_cstr, "cardinal2.apple.com.", sizeof(domainname_cstr));
106 // This function does memory cleanup and no verification.
109 mDNSPlatformMemFree(local_socket);
112 // This unit test starts a local only request for "cardinal2.apple.com.". It first
113 // calls start_client_request to start a query, it then verifies the
114 // req and query data structures are set as expected. Next, the cache is verified to
115 // be empty by AnswerNewLocalOnlyQuestion() and so results in GenerateNegativeResponse()
116 // getting called which sets up a reply with a negative answer in it for the client.
117 // On return from mDNS_Execute, the client's reply structure is verified to be set as
118 // expected. Lastly the timeout is simulated and mDNS_Execute is called. This results
119 // in a call to TimeoutQuestions(). And again, the GenerateNegativeResponse() is called
120 // which returns a negative response to the client. This time the client reply is verified
121 // to be setup with a timeout result.
122 - (void)testStartLocalOnlyClientQueryRequest
124 mDNS *const m = &mDNSStorage;
125 request_state* req = client_request_message;
126 char *msgptr = (char *)query_req_msgbuf;
127 size_t msgsz = sizeof(query_req_msgbuf);
129 mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
130 mStatus err = mStatus_NoError;
131 char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
132 struct reply_state *reply;
135 // Process the unit test's client request
136 start_client_request(req, msgptr, msgsz, query_request, local_socket);
137 XCTAssertEqual(err, mStatus_NoError);
139 // Verify the query initialized and request fields were set as expected
140 XCTAssertEqual(req->hdr.version, VERSION);
141 XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
142 XCTAssertEqual(req->flags, (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
143 XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexLocalOnly);
144 XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
146 q = &req->u.queryrecord.op.q;
147 XCTAssertEqual(q, m->NewLocalOnlyQuestions);
148 XCTAssertNil((__bridge id)m->Questions);
149 XCTAssertNil((__bridge id)m->NewQuestions);
150 XCTAssertEqual(q->SuppressUnusable, 1);
151 XCTAssertEqual(q->ReturnIntermed, 1);
152 XCTAssertEqual(q->Suppressed, mDNSfalse); // Regress <rdar://problem/27571734>
154 ConvertDomainNameToCString(&q->qname, qname_cstr);
155 XCTAssertFalse(strcmp(qname_cstr, domainname_cstr));
156 XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
158 XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
159 XCTAssertEqual(q->flags, req->flags);
160 XCTAssertEqual(q->qtype, 1);
161 XCTAssertEqual(q->qclass, 1);
162 XCTAssertEqual(q->LongLived, 0);
163 XCTAssertEqual(q->ExpectUnique, mDNSfalse);
164 XCTAssertEqual(q->ForceMCast, 0);
165 XCTAssertEqual(q->TimeoutQuestion, 1);
166 XCTAssertEqual(q->WakeOnResolve, 0);
167 XCTAssertEqual(q->UseBackgroundTraffic, 0);
168 XCTAssertEqual(q->ProxyQuestion, 0);
169 XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
170 XCTAssertNotEqual(q->StopTime, 0);
171 XCTAssertEqual(q->AppendSearchDomains, 0);
172 XCTAssertNil((__bridge id)q->DuplicateOf);
174 // At this point the the cache is empty. Calling mDNS_Execute will answer the local-only
175 // question with a negative response.
176 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
177 mDNS_Execute(m); // Regress <rdar://problem/28721294>
179 // Verify reply is a negative response and error code is set to kDNSServiceErr_NoSuchRecord error.
180 reply = req->replies;
181 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
183 XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
184 XCTAssertEqual(q->LOAddressAnswers, 0);
186 len = get_reply_len(qname_cstr, 0);
188 XCTAssertNil((__bridge id)reply->next);
189 XCTAssertEqual(reply->totallen, reply->mhdr->datalen + sizeof(ipc_msg_hdr));
190 XCTAssertEqual(reply->mhdr->version, VERSION);
191 XCTAssertEqual(reply->mhdr->datalen, len);
192 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
193 XCTAssertEqual(reply->mhdr->op, query_reply_op);
195 XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
196 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
197 XCTAssertEqual(reply->rhdr->error,
198 (DNSServiceErrorType)htonl(kDNSServiceErr_NoSuchRecord)); // Regress <rdar://problem/24827555>
200 // Simulate what udsserver_idle normally does for clean up
201 freeL("StartLocalOnlyClientQueryRequest:reply", reply);
204 // Simulate the query time out of the local-only question.
205 // The expected behavior is a negative answer with time out error
206 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
207 q->StopTime = mDNS_TimeNow_NoLock(m);
208 m->NextScheduledStopTime -= mDNSPlatformOneSecond*5;
211 // Verify the reply is a negative response with timeout error.
212 reply = req->replies;
213 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
214 XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
215 XCTAssertEqual(q->LOAddressAnswers, 0);
217 len = get_reply_len(qname_cstr, 0);
219 XCTAssertNil((__bridge id)reply->next);
220 XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
221 XCTAssertEqual(reply->mhdr->version, VERSION);
222 XCTAssertEqual(reply->mhdr->datalen, len);
223 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
224 XCTAssertEqual(reply->mhdr->op, query_reply_op);
225 XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
226 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
227 XCTAssertEqual(reply->rhdr->error,
228 (DNSServiceErrorType)htonl(kDNSServiceErr_Timeout)); // Regress <rdar://problem/27562965>
230 // Free request and reallocate to use when query is restarted
232 client_request_message = calloc(1, sizeof(request_state));
235 // This unit test populates the cache with four /etc/hosts records and then
236 // verifies there are four entries in the cache.
237 - (void)testPopulateCacheWithClientLOResponseRecords
239 mDNS *const m = &mDNSStorage;
241 // Verify cache is empty
242 int count = LogEtcHosts_ut(m);
243 XCTAssertEqual(count, 0);
245 // Populate /etc/hosts
246 mStatus result = InitEtcHostsRecords();
247 XCTAssertEqual(result, mStatus_NoError);
249 // mDNS_Execute is called to populate the /etc/hosts cache.
250 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
253 count = LogEtcHosts_ut(m);
254 XCTAssertEqual(count, 4);
256 [self _testRestartLocalOnlyClientQueryRequest]; // Continuation of this test
259 // This unit test starts a local only request for "cardinal2.apple.com.". It first
260 // calls start_client_request to start a query, it then verifies the
261 // req and query data structures are set as expected. Next, the cache is verified to
262 // contain the answer by AnswerNewLocalOnlyQuestion() and so results in setting up an
263 // answer reply to the client. On return from mDNS_Execute, the client's reply structure
264 // is verified to be set as expected. Lastly the timeout is simulated and mDNS_Execute is
265 // called. This results in a call to TimeoutQuestions(). And this time, the
266 // GenerateNegativeResponse() is called which returns a negative response to the client
267 // which specifies the timeout occurred. Again, the answer reply is verified to
268 // to specify a timeout.
269 - (void)_testRestartLocalOnlyClientQueryRequest
271 mDNS *const m = &mDNSStorage;
272 request_state* req = client_request_message;
273 char *msgptr = (char *)query_req_msgbuf;
274 size_t msgsz = sizeof(query_req_msgbuf); DNSQuestion *q;
275 mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
276 mStatus err = mStatus_NoError;
277 char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
278 struct reply_state *reply;
281 // Process the unit test's client request
282 start_client_request(req, msgptr, msgsz, query_request, local_socket);
283 XCTAssertEqual(err, mStatus_NoError);
285 XCTAssertEqual(req->hdr.version, VERSION);
286 XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
287 XCTAssertEqual(req->flags, (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
288 XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexLocalOnly);
289 XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
290 XCTAssertNil((__bridge id)m->Questions);
292 q = &req->u.queryrecord.op.q;
293 XCTAssertEqual(q, m->NewLocalOnlyQuestions);
294 XCTAssertEqual(q->SuppressUnusable, 1);
295 XCTAssertEqual(q->ReturnIntermed, 1);
296 XCTAssertEqual(q->Suppressed, mDNSfalse); // Regress <rdar://problem/27571734>
297 XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
298 XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
299 XCTAssertEqual(q->flags, req->flags);
300 XCTAssertEqual(q->qtype, 1);
301 XCTAssertEqual(q->qclass, 1);
302 XCTAssertEqual(q->LongLived, 0);
303 XCTAssertEqual(q->ExpectUnique, mDNSfalse);
304 XCTAssertEqual(q->ForceMCast, 0);
305 XCTAssertEqual(q->TimeoutQuestion, 1);
306 XCTAssertEqual(q->WakeOnResolve, 0);
307 XCTAssertEqual(q->UseBackgroundTraffic, 0);
308 XCTAssertEqual(q->ProxyQuestion, 0);
309 XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
310 XCTAssertNotEqual(q->StopTime, 0);
311 XCTAssertEqual(q->AppendSearchDomains, 0);
312 XCTAssertNil((__bridge id)q->DuplicateOf);
313 ConvertDomainNameToCString(&q->qname, qname_cstr);
314 XCTAssertFalse(strcmp(qname_cstr, domainname_cstr));
316 // Answer local-only question with found cache entry
317 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
318 mDNS_Execute(m); // Regress <rdar://problem/28721294>
319 XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
320 XCTAssertEqual(req->u.queryrecord.op.answered, 1);
321 XCTAssertEqual(q->LOAddressAnswers, 1);
322 XCTAssertEqual(q, m->LocalOnlyQuestions);
324 reply = req->replies;
325 len = get_reply_len(qname_cstr, 4);
327 XCTAssertNil((__bridge id)reply->next);
328 XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
329 XCTAssertEqual(reply->mhdr->version, VERSION);
330 XCTAssertEqual(reply->mhdr->datalen, len);
331 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
332 XCTAssertEqual(reply->mhdr->op, query_reply_op);
333 XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
334 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
335 XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
337 // Simulate the query time out of the local-only question.
338 // The expected behavior is a negative answer with time out error
339 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
340 q->StopTime = mDNS_TimeNow_NoLock(m);
341 m->NextScheduledStopTime -= mDNSPlatformOneSecond*5;
344 reply = req->replies->next;
345 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
346 XCTAssertNil((__bridge id)reply->next);
347 XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
348 XCTAssertEqual(q->LOAddressAnswers, 0);
349 len = get_reply_len(qname_cstr, 0);
351 XCTAssertNil((__bridge id)reply->next);
352 XCTAssertEqual(reply->totallen, len + + sizeof(ipc_msg_hdr));
353 XCTAssertEqual(reply->mhdr->version, VERSION);
354 XCTAssertEqual(reply->mhdr->datalen, len);
355 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
356 XCTAssertEqual(reply->mhdr->op, query_reply_op);
357 XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
358 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
359 XCTAssertEqual(reply->rhdr->error,
360 (DNSServiceErrorType)htonl(kDNSServiceErr_Timeout)); // Regress <rdar://problem/27562965>